package step

import (
	"bytes"
	"crypto/sha256"
	"github.com/wanchain/go-mpc/common"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	schcomm "github.com/wanchain/go-mpc/storeman/schnorrcomm"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type MpcRCommitStepUni struct {
	BaseStep
	RCommitMap map[*discover.NodeID]common.Hash
	RCommit    common.Hash
	sigNum     int
}

func CreateMpcRCommitStepUni(totalNum int, sigNum int) *MpcRCommitStepUni {
	log.SyslogInfo("CreateMpcRCommitStepUni begin")

	mpc := &MpcRCommitStepUni{
		*CreateBaseStep(totalNum, -1),
		make(map[*discover.NodeID]common.Hash, 0),
		common.Hash{}, sigNum}
	return mpc
}

func (ptStep *MpcRCommitStepUni) InitStep(result mpcprotocol.MpcResultInterface) error {
	log.SyslogDebug("MpcRCommitStepUni::InitStep begin", "ctx", ptStep.GetMpcContextId())
	ptStep.BaseStep.InitStep(result)

	_, grpIdString, err := ptStep.BaseStep.GetGrpId()
	if err != nil {
		log.SyslogErr("MpcRCommitStepUni", "error in GetGrpId", err.Error(), "ctx", ptStep.GetMpcContextId())
		return err
	}
	selfIndex, err := osmconf.GetOsmConf().GetSelfInx(grpIdString)
	if err != nil {
		log.SyslogErr("MpcRCommitStepUni::InitStep", "InitStep", err.Error(), "ctx", ptStep.GetMpcContextId())
		return err
	}

	var buf bytes.Buffer
	for sigIndex := 0; sigIndex < ptStep.sigNum; sigIndex++ {
		key := mpcprotocol.RPkShare + strconv.Itoa(int(selfIndex)) + strconv.Itoa(sigIndex)
		rpkshareBytes, err := result.GetByteValue(key)
		if err != nil {
			return err
		}
		buf.Write(rpkshareBytes[:])

	}
	ptStep.RCommit = sha256.Sum256(buf.Bytes())

	log.SyslogDebug("MpcRCommitStepUni::InitStep end", "ctx", ptStep.GetMpcContextId())
	return nil
}

func (ptStep *MpcRCommitStepUni) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("MpcRCommitStepUni::CreateMessage begin", "ctx", ptStep.GetMpcContextId())
	message := make([]mpcprotocol.StepMessage, 1)
	message[0].MsgCode = mpcprotocol.MPCMessage
	message[0].PeerID = nil

	var buf bytes.Buffer
	buf.Write(ptStep.RCommit[:])
	h := sha256.Sum256(buf.Bytes())

	prv, err := osmconf.GetOsmConf().GetSelfPrvKey()
	if err != nil {
		return nil
	}
	r, s, _ := schcomm.SignInternalData(prv, h[:])

	message[0].Data = make([]big.Int, 2)
	message[0].Data[0] = *r
	message[0].Data[1] = *s

	message[0].BytesData = make([][]byte, 1)
	message[0].BytesData[0] = ptStep.RCommit[:]
	log.SyslogDebug("MpcRCommitStepUni::CreateMessage end", "ctx", ptStep.GetMpcContextId())
	return message
}

func (ptStep *MpcRCommitStepUni) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogInfo("MpcRCommitStepUni::HandleMessage begin", "ctx", ptStep.GetMpcContextId())
	r := msg.Data[0]
	s := msg.Data[1]

	var buf bytes.Buffer
	buf.Write(msg.BytesData[0])
	h := sha256.Sum256(buf.Bytes())

	_, grpIdStr, err := ptStep.BaseStep.GetGrpId()
	if err != nil {
		log.SyslogErr("MpcRCommitStepUni", "HandleMessage error", err.Error(), "ctx", ptStep.GetMpcContextId())
	}

	senderPk, err := osmconf.GetOsmConf().GetPKByNodeId(grpIdStr, msg.PeerID)
	if err != nil {
		log.SyslogErr("MpcRCommitStepUni", "GetPKByNodeId error", err.Error(), "ctx", ptStep.GetMpcContextId())
	}

	bVerifySig := schcomm.VerifyInternalData(senderPk, h[:], &r, &s)

	if bVerifySig {
		log.SyslogInfo("MpcRCommitStepUni::HandleMessage check sig success", "ctx", ptStep.GetMpcContextId())
		ptStep.RCommitMap[msg.PeerID] = common.BytesToHash(msg.BytesData[0][:])
	} else {
		log.SyslogErr("MpcRCommitStepUni::HandleMessage check sig fail", "ctx", ptStep.GetMpcContextId())
	}

	log.SyslogInfo("MpcRCommitStepUni::HandleMessage end", "ctx", ptStep.GetMpcContextId())

	return true
}

func (ptStep *MpcRCommitStepUni) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {

	log.SyslogDebug("MpcRCommitStepUni::FinishStep begin", "ctx", ptStep.GetMpcContextId())

	// compute the intersec and save
	err := ptStep.BaseStep.FinishStep()
	if err != nil {
		return err
	}

	_, grpIdString, err := ptStep.BaseStep.GetGrpId()
	if err != nil {
		log.SyslogErr("MpcRCommitStepUni", "error in GetGrpId", err.Error(), "ctx", ptStep.GetMpcContextId())
		return err
	}

	for nodeId, value := range ptStep.RCommitMap {
		nodeIndex, err := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, nodeId)
		if err != nil {
			return err
		}
		key := mpcprotocol.RCommit + strconv.Itoa(int(nodeIndex))
		err = result.SetByteValue(key, value.Bytes())
		if err != nil {
			return err
		}
		log.SyslogDebug("MpcRCommitStepUni::FinishStep SaveRCommit successfully",
			"key", key,
			"value", common.ToHex(value.Bytes()),
			"nodeIndex", nodeIndex,
			"ctx", ptStep.GetMpcContextId())
	}

	return nil
}
